package com.tengxing.e1.e1_usb_camerademo;

import static com.serenegiant.utils.ThreadPool.queueEvent;

import android.Manifest;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.PixelCopy;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.utils.HandlerThreadHandler;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Main_Activity";
    private static final boolean DEBUG = true;	// TODO set false when production
    TextView tv_dev_list;
    Button btn_cam_cap, btn_cam_preview;
    boolean usbCamIsPreview = true;
    private UsbManager usbManager;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private static final int USB_PERMISSION_REQUEST_CODE = 1001;

    //===== USB Camera Start =====
    private final Object mSync = new Object();
    // for accessing USB and USB camera
    private USBMonitor mUSBMonitor;
    private UVCCamera mUVCCamera;
    private SurfaceView mUVCCameraView;
    // for open&start / stop&close camera preview
    //private ImageButton mCameraButton;
    private Surface mPreviewSurface;
    private boolean isActive, isPreview;
    private Handler mWorkerHandler;
    private long mWorkerThreadID = -1;
    //===== USB Camera End =====

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        USB_Camera_Init();
    }
    @Override
    protected void onStart() {
        super.onStart();
        synchronized (mSync) {
            if (mUSBMonitor != null) {
                mUSBMonitor.register();

            }
            if (mUVCCamera != null){
                mUVCCamera.startPreview();
            }
        }
    }

    @Override
    protected void onStop() {
        synchronized (mSync) {
            if (mUSBMonitor != null) {
                mUSBMonitor.unregister();
            }
            if (mUVCCamera != null) {
                mUVCCamera.stopPreview();
            }
        }
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        synchronized (mSync) {
            isActive = isPreview = false;
            if (mUVCCamera != null) {
                mUVCCamera.destroy();
                mUVCCamera = null;
            }
            if (mUSBMonitor != null) {
                mUSBMonitor.destroy();
                mUSBMonitor = null;
            }
        }
        mUVCCameraView = null;
        super.onDestroy();
    }

    private final BroadcastReceiver mUsbPermissionActionReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        //user choose YES for your previously popup window asking for grant perssion for this usb device
                        if(null != usbDevice){
                            afterGetUsbPermission(usbDevice);
                        }
                    }
                    else {
                        //user choose NO for your previously popup window asking for grant perssion for this usb device
                        Toast.makeText(context, String.valueOf("Permission denied for device" + usbDevice), Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }
    };

    void InitView(){
        tv_dev_list = findViewById(R.id.tv_dev_list);
        btn_cam_cap = findViewById(R.id.btn_cam_cap);
        btn_cam_preview = findViewById(R.id.btn_cam_preview);
    }

    void USB_Camera_Init(){
        InitView();
        usbManager = (UsbManager) getSystemService(USB_SERVICE);
        requestStoragePermission();
        Get_Camera_Permission();
        Get_USB_Permission();
        Get_USB_Devices();
        //===== USB Camera Start =====
        if(Check_Camera_Permission()){//如果有相机权限就启动相机
            if (mWorkerHandler == null) {
                mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
                mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
            }

            mUVCCameraView = (SurfaceView)findViewById(R.id.surfaceView_Cam);
            mUVCCameraView.getHolder().addCallback(mSurfaceViewCallback);
            mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener);//serenegiant usb camera*/
        }
        btn_cam_cap.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                captureSurfaceView(mUVCCameraView);
            }
        });
        btn_cam_preview.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(usbCamIsPreview){
                    mUVCCamera.stopPreview();
                    usbCamIsPreview = false;
                    btn_cam_preview.setText("开始预览");
                }else{
                    mUVCCamera.setPreviewDisplay(mUVCCameraView.getHolder().getSurface());
                    mUVCCamera.startPreview();
                    usbCamIsPreview = true;
                    btn_cam_preview.setText("停止预览");
                }

            }
        });
        //===== USB Camera End =====
    }

    //===== USB Camera Picture Save Start =====
    private static final int REQUEST_WRITE_STORAGE = 1;

    private void requestStoragePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_WRITE_STORAGE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_WRITE_STORAGE && grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限已授予
        }
    }

    public void captureSurfaceView(SurfaceView surfaceView) {
        //Bitmap bitmap = Bitmap.createBitmap(2560, 1440, Bitmap.Config.ARGB_8888);
        Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(), Bitmap.Config.ARGB_8888);
        PixelCopy.request(surfaceView, bitmap, copyResult -> {
            if (copyResult == PixelCopy.SUCCESS) {
                saveBitmapToStorage(bitmap);
            }
        }, new Handler(Looper.getMainLooper()));
    }

    private void saveBitmapToStorage(Bitmap bitmap) {

        String path= Environment.getExternalStorageDirectory()+"/DCIM/E1_USB_CameraDemo/";
        File mImageFile=new File(path);
        if(!mImageFile.exists()){
            mImageFile.mkdir();     //如果路径不存在就建一个
        }
        String timeStamp=new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String fileName="IMG_E1_CameraDemo_"+timeStamp+".jpg";

        File imageFile = new File(path, fileName);

        try (FileOutputStream fos = new FileOutputStream(imageFile)) {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            Toast.makeText(this, "图片已保存至：" + imageFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Log.e(TAG, "saveBitmapToStorage:"+e);
        }
    }
    //===== USB Camera Picture Save End =====

    //===== USB Camera Start =====
    private final SurfaceHolder.Callback mSurfaceViewCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(final SurfaceHolder holder) {
            if (DEBUG) Log.v(TAG, "surfaceCreated:");
        }

        @Override
        public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
            if ((width == 0) || (height == 0)) return;
            if (DEBUG) Log.v(TAG, "surfaceChanged:");
            mPreviewSurface = holder.getSurface();
            synchronized (mSync) {
                if (isActive && !isPreview && (mUVCCamera != null)) {
                    mUVCCamera.setPreviewDisplay(mPreviewSurface);
                    mUVCCamera.startPreview();
                    isPreview = true;
                }
            }
        }

        @Override
        public void surfaceDestroyed(final SurfaceHolder holder) {
            if (DEBUG) Log.v(TAG, "surfaceDestroyed:");
            synchronized (mSync) {
                if (mUVCCamera != null) {
                    mUVCCamera.stopPreview();
                }
                isPreview = false;
            }
            mPreviewSurface = null;
        }
    };

    private final USBMonitor.OnDeviceConnectListener mOnDeviceConnectListener = new USBMonitor.OnDeviceConnectListener() {
        @Override
        public void onAttach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onAttach: dev="+ device.getDeviceId() + " pd_name:" + device.getProductName());
            Toast.makeText(MainActivity.this, "USB_DEVICE_ATTACHED\ndev="+ device.getDeviceId() + " pd_name:" + device.getProductName(), Toast.LENGTH_SHORT).show();
            mUSBMonitor.requestPermission(device);
        }

        @Override
        public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew) {

            if (DEBUG) Log.v(TAG, "onConnect:" + device.getProductName());
            if(device.getProductName() == null){
                if (DEBUG) Log.d(TAG, "onConnect: no usb dev");
                return;
            }
            if(device.getProductName().contains("Camera") || device.getProductName().contains("camera")){
                if (DEBUG) Log.d(TAG, "onConnect: usb cam dev find.");
            }else{
                if (DEBUG) Log.d(TAG, "onConnect: no usb cam dev.");
                return;
            }
            synchronized (mSync) {
                if (DEBUG) Log.d(TAG, "mUVCCamera: mSync");
                if (mUVCCamera != null) {
                    if (DEBUG) Log.d(TAG, "mUVCCamera: not null");
                    mUVCCamera.destroy();
                }
                isActive = isPreview = false;
            }
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    if (DEBUG) Log.d(TAG, "queueEvent:" + device.getProductName());
                    synchronized (mSync) {
                        final UVCCamera camera = new UVCCamera();
                        camera.open(ctrlBlock);
                        if (DEBUG) Log.d(TAG, "supportedSize:" + camera.getSupportedSize());
                        try {
                            camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG);
                        } catch (final IllegalArgumentException e) {
                            try {
                                // fallback to YUV mode
                                camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE);
                            } catch (final IllegalArgumentException e1) {
                                camera.destroy();
                                return;
                            }
                        }
                        mPreviewSurface = mUVCCameraView.getHolder().getSurface();
                        if (mPreviewSurface != null) {
                            isActive = true;
                            camera.setPreviewDisplay(mPreviewSurface);
                            camera.startPreview();
                            isPreview = true;
                        }
                        synchronized (mSync) {
                            mUVCCamera = camera;
                        }
                    }
                }
            });
        }

        @Override
        public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock) {
            if (DEBUG) Log.v(TAG, "onDisconnect:");
            // XXX you should check whether the comming device equal to camera device that currently using
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    synchronized (mSync) {
                        if (mUVCCamera != null) {
                            mUVCCamera.close();
                            if (mPreviewSurface != null) {
                                mPreviewSurface.release();
                                mPreviewSurface = null;
                            }
                            isActive = isPreview = false;
                        }
                    }
                }
            });
        }

        @Override
        public void onDettach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onDettach:");
            Toast.makeText(MainActivity.this, "USB_DEVICE_DETACHED", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel(final UsbDevice device) {
        }
    };

    //===== USB Camera End =====





    boolean Check_Camera_Permission(){
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
    }

    void Get_Camera_Permission(){
        final int REQUEST_CAMERA_CODE = 100;
        if ( !Check_Camera_Permission() ) {
            //申请相机权限
            requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_CODE);
        }
    }

    void Get_USB_Devices(){
        String usb_dev_str="USB Device List:\n";

        for(UsbDevice usbDevice : usbManager.getDeviceList().values()){
            usb_dev_str = usb_dev_str.concat("Dev:" + usbDevice.getDeviceName() +
                    "  Dev_id:"+ usbDevice.getDeviceId() +
                    "  Class:" + usbDevice.getDeviceClass() +
                    " Sub_Class:" + usbDevice.getDeviceSubclass() +


                    "\nPd_Name:"+ usbDevice.getProductName()+
                    "  MF_Name:" + usbDevice.getManufacturerName()+
                    "\nDev_Pt:" + usbDevice.getDeviceProtocol() +
                    "  SN:" + usbDevice.getSerialNumber()+
                    "  Ver:" + usbDevice.getVersion()+
                    "  VID:" + usbDevice.getVendorId()+
                    "  Pd_ID:" + usbDevice.getProductId()+
                    "\n\n");
        }
        tv_dev_list.setText(usb_dev_str);
    }

    void USB_Camera_Start_Preview(){

    }

    void Get_USB_Permission(){
        IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION);
        //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            registerReceiver(mUsbPermissionActionReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED);
        //}
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, USB_PERMISSION_REQUEST_CODE, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
        //here do emulation to ask all connected usb device for permission
        for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
            //add some conditional check if necessary
            //if(isWeCaredUsbDevice(usbDevice)){
            if(DEBUG) Log.d(TAG, usbDevice.getDeviceId()+" permission: "+ usbManager.hasPermission(usbDevice));
            if(usbManager.hasPermission(usbDevice)){
                //if has already got permission, just goto connect it
                //that means: user has choose yes for your previously popup window asking for grant perssion for this usb device
                //and also choose option: not ask again
                afterGetUsbPermission(usbDevice);
                if(DEBUG) Log.d(TAG, usbDevice.getDeviceId()+" have usb permission");
            }else{
                //this line will let android popup window, ask user whether to allow this app to have permission to operate this usb device

                if(DEBUG) Log.d(TAG, usbDevice.getDeviceId()+" request usb permission");
                usbManager.requestPermission(usbDevice, mPermissionIntent);

            }
            //}
        }
    }

    private void afterGetUsbPermission(UsbDevice usbDevice){
        //call method to set up device communication
        doYourOpenUsbDevice(usbDevice);
    }

    private void doYourOpenUsbDevice(UsbDevice usbDevice){
        //now follow line will NOT show: User has not given permission to device UsbDevice
        //UsbDeviceConnection connection = usbManager.openDevice(usbDevice);
        //add your operation code here
    }

}